﻿using Application.Commons.Data;
using AutoMapper;
using FrameworkCore.Extensions;
using FrameworkCore.Helpers;
using FrameworkCore.Models;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using NM.Module.Auth.API.Configurations;
using NM.Module.Auth.API.DTOs;
using NM.Module.Auth.API.Interfaces;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using System.Text;

namespace NM.Module.Auth.API.Commands.RefreshToken;

#nullable disable

/// <summary>
/// 刷新令牌
/// </summary>
public class RefreshTokenCommand : IRequest<Result<LoginOutputModel>>
{
    /// <summary>
    /// 刷新令牌
    /// </summary>
    [Required(ErrorMessage = "刷新令牌是必填的")]
    public string RefreshToken { get; set; }
}

public class RefreshTokenCommandHandler : IRequestHandler<RefreshTokenCommand, Result<LoginOutputModel>>
{
    private readonly ApplicationDbContext _context;
    private readonly IMapper _mapper;
    private readonly ITokenService _tokenService;
    private readonly IOptions<JwtSettings> _optJwtSettings;

    public RefreshTokenCommandHandler(
        ApplicationDbContext context,
        IMapper mapper, ITokenService
        tokenService, IOptions<JwtSettings> optJwtSettings)
    {
        _context = context;
        _mapper = mapper;
        _tokenService = tokenService;
        _optJwtSettings = optJwtSettings;
    }

    public async Task<Result<LoginOutputModel>> Handle(RefreshTokenCommand request, CancellationToken cancellationToken)
    {
        #region 验签
        var jwtSecurityToken = _tokenService.Decode(request.RefreshToken);
        var claims = jwtSecurityToken?.Claims?.ToArray();
        ExceptionHelper.ThrowIfFalse(claims.Any(), "登录过期，请重新登录！");

        var refreshExpires = claims!.FirstOrDefault(a => a.Type == "exp")?.Value;
        ExceptionHelper.ThrowIfTrue(refreshExpires!.IsNullOrEmpty() ||
            refreshExpires!.ToInt64OrDefault() <= DateTime.Now.ToUnixTimestampSeconds(), "登录过期，请重新登录！");

        var securityKey = _optJwtSettings.Value.SecurityKey;
        var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(securityKey)), SecurityAlgorithms.HmacSha256);
        var payload = jwtSecurityToken!.RawHeader + "." + jwtSecurityToken.RawPayload;
        ExceptionHelper.ThrowIfFalse(jwtSecurityToken.RawSignature ==
            JwtTokenUtilities.CreateEncodedSignature(payload, signingCredentials), "登录过期，请重新登录！");
        #endregion

        #region 刷新令牌
        var customerId = claims![0].Value.ToInt64OrDefault();
        var customer = await _context.Customers.FirstOrDefaultAsync(q => q.Id == customerId);
        ExceptionHelper.ThrowIfNull(customer, "账号已被禁用");

        List<Claim> newClaims = new()
        {
            new Claim(ClaimTypes.NameIdentifier, customer.Id.ToString()),
            new Claim(ClaimTypes.Name, customer.Username)
        };
        int expires = 0;
        var result = _mapper.Map<LoginOutputModel>(customer);

        (result.RefreshToken, expires) = await _tokenService.BuildAsync(claims, null, true);
        (result.AccessToken, expires) = await _tokenService.BuildAsync(claims);
        result.Expires = DateTime.Now.AddSeconds(expires).ToString("yyyy/MM/dd HH:mm:ss");
        return await Result<LoginOutputModel>.SucceedAsync(result);
        #endregion
    }
}